搜索 K
Appearance
博客正在加载中...
Appearance
sed 号称文本处理三剑客之一,能有效提高我们的效率
阅读之前,读者应该有以下知识:
命令行基础(懂得如何打开 cmd 和使用命令)
sed 可以在 Linux 或 Git Bash 下运行
一点点正则表达式的基础
当然,相信读到本文的人,既然都接触到 sed 了,一般都有一定的编程经验。
本文较长,可以分段阅读。
sed 是什么?
awk、grep、sed 是 Linux 操作文本的三大利器,合称文本三剑客,也是必须掌握的 Linux 命令之一。三者的功能都是处理文本,但侧重点各不相同,其中属 awk 功能最强大,但也最复杂。awk 更适合格式化文本,grep 更适合单纯的查找或匹配文本,sed 更适合编辑匹配到的文本。

sed 的工作原理很简单:在处理文本时逐行读取文件内容,读到匹配的行就根据指令做操作,不匹配就跳过。
调用 sed 命令的语法有两种:
sed + 选项 '指令' 文件sed + 选项 -f 包含 sed 指令的文件 文件以下选项读者看看即可,有个印象,后面我们详细介绍
sed 的常用选项:
| 选项 | 含义 |
|---|---|
| -e | 告诉 sed 将下一个参数解释为一个 sed 指令,只有当命令行上给出多个 sed 指令时才需要使用 -e 选项 |
| -f | 后跟保存了 sed 指令的文件 |
| -i | 直接对内容进行修改,不加 -i 时默认只是预览,不会对文件做实际修改 |
| -n | 取消默认输出,sed 默认会输出所有文本内容,使用 -n 参数后只显示处理过的行 |
sed 中的常用指令:
| 命令 | 含义 |
|---|---|
| a | 追加,向匹配行后面插入内容 |
| i | 插入,向匹配行前插入内容 |
| c | 更改,更改匹配行的内容 |
| d | 删除,删除匹配的内容 |
| s | 替换,替换掉匹配的内容 |
| p | 打印,打印出匹配的内容,通常与 -n 选项和用 |
| = | 用来打印被匹配的行的行号 |
| n | 读取下一行,遇到 n 时会自动跳入下一行 |
| r,w | 读和写编辑命令,r 用于将内容读入文件,w 用于将匹配内容写入到文件 |
我们先准备下环境,如果在 Linux 上一般可直接使用,如果是 Windows 下可以用 Git bash。
我们先新建一个 test.txt 文件,里面的内容如下:
11
22
33
44
55
22
然后我们就可以直接开始示范了。
前排提醒,例子有点多,不一定要全部一次性看完,建议看前面 2~3 个示范,懂个大概就行了。用到时再来看也不迟。
sed 'lahello' test.txt #其中,'lahello' 中的数字 1 表示行号,a 表示添加 add,所以该命令的作用就是:向第一行后面添加 hello。
我们回顾下 sed 命令的格式: sed + 选项 '指令' 文件。 这里我们没用选项,只用了指令 lahello,而 test.txt 就是文件。
我们测试下:
# 原始文件内容
$ cat test.txt
11
22
33
44
55
22
# 使用sed 命令后输出,
$ sed '1ahello' test.txt
11
hello
22
33
44
55
22
# 没有修改原始内容,因为没有用 -i 选项 保存文件内容
$ cat test.txt
11
22
33
44
55
22
因为我们没用在 -i 选项,所以不会保存插入 hello 的内容到文件里,第二条指令 sed '1ahello' test.txt 的输出只是预览。
现在我们试着保存:
$ sed -i '1ahello' test.txt
$ cat test.txt
11
hello
22
33
44
55
22
可以看到文件内确实添加了内容。
为了方便后面的测试,我们删除第文件第 2 行的 hello(重新初始化文件)。
sed '/22/achina' test.txt # 向文件内容为 22 的下一行添加 china,如果文件中有多行的内容为 22,则每一行后面都会添加# 原始文件内容
$ cat test.txt
11
22
33
44
55
22
# 在每一行后都添加了 china
$ sed '/22/achina' test.txt
11
22
china
33
44
55
22
china
在文件最后一行添加内容:使用 $
$ sed '$afuk' test.txt
11
22
33
44
55
22
fuk
-i 命令表示在匹配行之前插入
sed '3igood' test.txt # 在第三行之前插入good# 原始文件内容
$ cat test.txt
11
22
33
44
55
22
$ sed '3igood' test.txt
11
22
good
33
44
55
22
在包含 china 之前的行插入数据,如果含多个,则都会插入
sed '/22/inice' test.txt # 在内容为22的内容之前的行插入nice,如果含多个,则都会插入
# 原始文件内容
$ cat test.txt
11
22
33
44
55
22
$ sed '/22/inice' test.txt
11
nice
22
33
44
55
nice
22# 原始文件内容
$ cat test.txt
11
22
33
44
55
22
$ sed '$inice' test.txt
11
22
33
44
55
nice
22
c 命令用于更改匹配行的内容
举例:
sed '5cfive' test.txt # 将第 5 行的替换为 five测试:
# 原始文件内容
$ cat test.txt
11
22
33
44
55
22
$ sed '5cfive' test.txt
11
22
33
44
five
22举例:
sed '/22/ctwo' test.txt #将包含22的全部替换为two测试
# 原始文件内容
$ cat test.txt
11
22
33
44
55
22
$ sed '/22/ctwo' test.txt
11
two
33
44
55
two
d 命令表示删除匹配行
sed '7d' test.txt #删除第 7 行的数据# 原始文件内容
$ cat test.txt
11
22
33
44
55
22
$ sed '6d' test.txt
11
22
33
44
55
sed '1~2d' test.txt #从第一行开始删除,每隔两行删掉一个,即删掉奇数行# 原始文件内容
$ cat test.txt
11
22
33
44
55
22
$ sed '1~2d' test.txt
22
44
22
sed '1,2d' test.txt #删掉 1-2 行# 原始文件内容
$ cat test.txt
11
22
33
44
55
22
$ sed '1,2d' test.txt
33
44
55
22
sed '1,2!d' test.txt #删除除了 1-2 行之外的行# 原始文件内容
$ cat test.txt
11
22
33
44
55
22
$ sed '1,2!d' test.txt
11
22
sed '$d' test.txt # 删除最后一行
# 原始文件内容
$ cat test.txt
11
22
33
44
55
22
$ sed '$d' test.txt
11
22
33
44
55
sed '/china/!d' test.txt #删除 除了包含china之外的行# 原始文件内容
$ cat test.txt
11
22
33
44
55
22
$ sed '/22/!d' test.txt
22
22
删除匹配 22 的行以及下一行
sed '/22/,+1d' test.txt #删除匹配 22 的行以及下一行# 原始文件内容
$ cat test.txt
11
22
33
44
55
22
$ sed '/22/,+1d' test.txt
11
44
55
删除从匹配到 22 的行到最后一行
sed '/22/,$d' test.txt #删除从匹配到 22 的行到最后一行# 原始文件内容
$ cat test.txt
11
22
33
44
55
22
$ sed '/22/,$d' test.txt
11
sed '/^$/d' test.txt #删除空行 ( ^表示开始,$ 表示结尾,^$ 表示没有内容的行,因此匹配的是空行)$ cat test.txt
11
22
33
44
55
22
[wasadmin@zbapp1 ~]<20221229 17:55:56>$ sed '/^$/d' test.txt
11
22
33
44
55
22
sed '/22\|^$/!d' test.txt # 删除不匹配 22 或者空行的行。 先匹配 22 或空行, 然后!表示取反$ cat test.txt
11
22
33
44
55
22
[wasadmin@zbapp1 ~]<20221229 18:06:04>$ sed '/22\|^$/!d' test.txt
22
22
sed '1,5{/22/d}' test.txt #删除1~5行中,匹配内容22的行$ cat test.txt
11
22
33
44
55
22
$ sed '1,5{/22/d}' test.txt
11
33
44
55
22
s 命令用户替换匹配的内容(和 c 命令不同,C 命令是整个行都替换掉)
示例:like 替换成 love,默认只替换每行匹配到的第一个
sed 's/like/love/' test.txt$ cat test.txt
11
I like spider man
33
44
55
I like emilia, like rem
$ sed 's/like/love/' test.txt
11
I love spider man
33
44
55
I love emilia, like rem可以看到最后一行,还有一个 like 没被替换
我们可以加个 2 表明将第二个匹配到的内容替换:
sed 's/like/love/2' test.txt # 将每行第二个匹配到的like替换成love$ cat test.txt
11
I like spider man
33
44
55
I like emilia, like rem
$ sed 's/like/love/2' test.txt
11
I like spider man
33
44
55
I like emilia, love rem
g 可将所有 like 替换成 love
$ cat test.txt
11
I like spider man
33
44
55
I like emilia, like rem
$ sed 's/like/love/g' test.txt
11
I love spider man
33
44
55
I love emilia, love rem可以看到最后一行的 like 也被替换成 love 了
看一个稍微复杂一点的脚本。将每行中所有匹配的 china 替换成中国,并将替换后的行采用覆写的方式写入 yi.txt 文件中。
sed -n 's/china/中国/gpw yi.txt' test.txt
sed -n 's/china/zhongguo/gpw yi.txt' test.txt$ cat -n test.txt
1 11
2 hello
3
4 I love china love
5 china
6
7 *******
8 I love china
9 china
$ cat yi.txt
1
2
3
4$ sed -n 's/china/zhongguo/gpw yi.txt' test.txt
I love zhongguo love
zhongguo
I love zhongguo
zhongguo
$ cat yi.txt
I love zhongguo love
zhongguo
I love zhongguo
zhonggug
sed '/#/s/,.*//g' test.txt # 匹配有‘#’号的行,替换匹配行中逗号后的所有内容为空 (,.*)表示逗号后的所在内容$ cat 1.txt
# comment,Hello
1.txt first line
1.txt second line
$ sed '/#/s/,.*//g' 1.txt
# comment
1.txt first line
1.txt second line
sed 's/..$//g' test.txt # 每个点代表一个字符,$表示匹配末尾 (..$)表示匹配最后两个字符, 两个 // 中间是没有内容的,表示用空字符替换$ cat test.txt
11
I like spider man
33
44
55
I like emilia, like rem
$ sed 's/..$//g' test.txt
I like spider m
I like emilia, like r
sed 's/^#.*//' test.txt # ( ^#)表示匹配以#开头,(.*)代表所有内容$ cat 1.txt
# comment
1.txt first line
1.txt second line
$ sed 's/^#.*//' 1.txt
1.txt first line
1.txt second line
sed 's/^#.*//;/^$/d' test.txt # 先替换test.txt文件中所有注释的空行为空行,然后删除空行,替换和删除操作中间用分号隔开$ cat 1.txt
# comment
1.txt first line
1.txt second line
$ sed 's/^#.*//;/^$/d' 1.txt
1.txt first line
1.txt second line
sed 's/^[0-9]/(&)/' yi.txt #将每一行中行首的数字加上一个小括号 (^[0-9])表示行首是数字,& 符号代表匹配的内容$ cat test.txt
11
I like spider man
33
44
55
I like emilia, like rem
$ sed 's/^[0-9]/(&)/' test.txt
(1)1
I like spider man
(3)3
(4)4
(5)5
I like emilia, like rem打印:n
除了 grep 用 sed 也能打印匹配的行,并且更灵活。使用示例如下,不一一演示了。
指定行数打印:
sed -n '4p' test.txt #打印文件中的第 4 行内容
sed -n '2~2p' test.txt #从第二行开始,每隔两行打印一行,波浪号后面的 2 表示步长
sed -n '$p' test.txt #打印文件的最后一行
sed -n '1,3p' test.txt #打印 1 到 3 行
sed -n '3,$p' test.txt #打印从第 3 行到最后一行的内容打印匹配到的行:
sed -n '/love/p' test.txt #逐行读取文件,打印匹配love的行
sed -n '/china/,3p' test.txt #逐行读取文件,打印从匹配china的行到第3行的内容
sed -n '1,/china/p' test.txt #打印第一行到匹配china的行
sed -n '/\*/,$p' test.txt #打印从匹配*的行到最后一行的内容
sed -n '/\*/,+1p' test.txt #打印匹配*的行及其向后一行,如果有多行匹配too,则匹配的每一行都会向后多打印一行
sed -n '/china/,/\*/p' test.txt #打印从匹配内容china到匹配内容*的行
等号 = 可以指定打印行号
$ cat test.txt
11
I like spider man
33
44
55
I like emilia, like rem
$ sed -n '=' test.txt
1
2
3
4
5
6共 6 行,打印了 6 个行号
我们可以打印「用正则匹配到的行」的行号,例如
sed -n "$=" test.txt # 打印文件最后一行的行号
sed -n '/like/=' test.txt #打印匹配like的行的行号$ cat -n test.txt
1 11
2 I like spider man
3 33
4 44
5 55
6 I like emilia, like rem
$ sed -n '/like/=' test.txt
2
6
打印匹配 like 的行的行号和内容(可用于查看日志中有 like 的行及其内容)
sed -n '/like/{=;p}' test.txt$ cat -n test.txt
1 11
2 I like spider man
3 33
4 44
5 55
6 I like emilia, like rem
$ sed -n '/like/{=;p}' test.txt
2
I like spider man
6
I like emilia, like rem
r 命令可用于读入内容,可以理解为插入某个文件的内容
将文件 2.txt 中的内容,读入 1.txt 中,会在 1.txt 中的每一行后都读入 2.txt 的内容
sed 'r 2.txt' 1.txt$ cat 1.txt
1.txt first line
1.txt second line
$ cat 2.txt
2.txt first line
2.txt second line
$ sed 'r 2.txt' 1.txt
1.txt first line
2.txt first line
2.txt second line
1.txt second line
2.txt first line
2.txt second line
在 1.txt 第 2 行之后插入文件 2.txt 的内容:格式
sed '3r 2.txt' 1.txt$ sed '2r 2.txt' 1.txt
1.txt first line
1.txt second line
2.txt first line
2.txt second line
在 1.txt 的最后一行插入 2.txt 的内容:
sed '$r 2.txt' 1.txt$ sed '$r 2.txt' 1.txt
1.txt first line
1.txt second line
2.txt first line
2.txt second line注意,如果指定的行数超过了原本的文件的行数,插入不会生效。例如 1.txt 只有 2 行,如果要在第三行后插入文件 2.txt 的内容,不会生效:
$ sed '3r 2.txt' 1.txt
1.txt first line
1.txt second line
在匹配 first 的行之后插入文件 2.txt 的内容,如果 1.txt 中有多行匹配则在每一行之后都会插入
sed '/first/r 2.txt' 1.txt$ cat 1.txt
1.txt first line
1.txt second line
$ cat 2.txt
2.txt first line
2.txt second line
$ sed '/first/r 2.txt' 1.txt
1.txt first line
2.txt first line
2.txt second line
1.txt second line
sed -n 'w 2.txt' 1.txt # 将 1.txt 文件的内容写入 2.txt 文件,如果 2.txt 文件不存在则创建,如果 2.txt 存在则覆盖
sed -n -e '1w 2.txt' -e '$w 2.txt' 1.txt # 将 1.txt 的第 1 行和最后一行内容写入 2.txt
sed -n -e '1w 2.txt' -e '$w 3.txt' 1.txt # 将 1.txt 的第 1 行和最后一行分别写入 2.txt 和 3.txt
sed -n '/abc\|123/w 2.txt' 1.txt # 将 1.txt 中匹配 abc 或 123 的行的内容,写入到 2.txt 中
sed -n '/666/,$w 2.txt' 1.txt # 将 1.txt 中从匹配 666 的行到最后一行的内容,写入到 2.txt中
sed -n '/xyz/,+2w 2.txt' 1.txt # 将 1.txt 中从匹配 xyz 的行及其后 2 行的内容,写入到 2.txt中
批量更改当前目录中的文件后缀名(多种实现方式):
#!/bin/bash
if [ $# -ne 2 ];then #判断用户的输入,如果参数个数不为 2 则打印脚本用法
echo "Usage:$0 + old-file new-file"
exit
fi
for i in *$1* #对包含用户给定参数的文件进行遍历
do
if [ -f $i ];then
iname=`basename $i` # 获取文件名
newname=`echo $iname | sed -e "s/$1/$2/g"` #对文件名进行替换并赋值给新的变量
mv $iname $newname # 对文件进行重命名
fi
done
exit 666#!/bin/bash
read -p "input the old file:" old # 提示用户输入要替换的文件后缀
read -p "input the new file:" new
[ -z $old ] || [ -z $new ] && echo "error" && exit #判断用户是否有输入,如果没有输入则打印 error 并退出
for file in `ls *.$old`
do
if [ -f $file ];then
newfile=${file%$old} # 对文件进行去尾
mv $file ${newfile}$new # 文件重命名
fi
done#!/bin/bash
if [ $# -ne 2 ];then # 判断位置变量的个数是是否为 2
echo "Usage:$0 old-file new-file"
exit
fi
for file in `ls` # 在当前目录中遍历文件
do
if [[ $file =~ $1$ ]];then # 对用户给出的位置变量 $1 进行正则匹配,$1$ 表示匹配以变量 $1 的值为结尾的文件
echo $file # 将匹配项输出到屏幕进行确认
new=${file%$1} # 对文件进行去尾处理,去掉文件后缀保留文件名,并将文件名赋给变量 new
mv $file ${new}$2 # 将匹配文件重命名为:文件名 + 新的后缀名
fi
done#!/bin/bash
if [ $# -ne 2 ];then #判断位置变量的个数是是否为 2
echo "Usage:$0 old-file new-file"
exit
fi
for file in `ls` #在当前目录中遍历文件
do
if [[ $file =~ $1$ ]];then #对用户给出的位置变量 $1 进行正则匹配,$1$ 表示匹配以变量 $1 的值为结尾的文件
echo $file #将匹配项输出到屏幕进行确认
new=${file%$1} #对文件进行去尾处理,去掉文件后缀保留文件名,并将文件名赋给变量 new
mv $file ${new}$2 #将匹配文件重命名为:文件名+新的后缀名
fi
done